#include "Config.h"
using namespace d_utils;

const string Config::DELIMITERS = " \t\a\n";

Config::Config()
{
	initialized = false;
}

Config::Config(const string &fileName)
{
	if(parseFile(fileName) == 0)
	{
		initialized = true;
	}
	else
	{
		initialized = false;
	}
}

Config::~Config()
{
	confTable.clear();
	varTable.clear();
	initialized = false;
}

string Config::operator [](const string &key)
{
	return get(key);
}

string Config::get(const string &key)
{
	map<string, string>::iterator it = confTable.find(key);
	if(it == confTable.end())
		return "";
	string ret = it->second;
	replaceVariable(ret);
	return ret;
}

string Config::get(const string &key, const string &def)
{
	string ret = get(key);
	if(ret == "")
		return def;
	else
		return ret;
}

int Config::getInt(const string &key)
{
	//how to handle exceptional integer?
	//zero as fallback is not a good solution
	string tmp = get(key);
	if(tmp == "")
		return 0;
	istringstream iss(tmp);
	int ret = 0;
	iss >> ret;
	return ret;
}

int Config::getInt(const string &key, int def)
{
	string tmp = get(key);
	if(tmp == "")
		return def;
	istringstream iss(tmp);
	int ret = def;
	iss >> ret;
	return ret;
}

float Config::getFloat(const string &key)
{
	string tmp = get(key);
	if(tmp == "")
		return 0L;
	istringstream iss(tmp);
	float ret = 0L;
	iss >> ret;
	return ret;
}

float Config::getFloat(const string &key, float def)
{
	string tmp = get(key);
	if(tmp == "")
		return def;
	istringstream iss(tmp);
	float ret = def;
	iss >> ret;
	return ret;
}

int Config::addConfFile(const string &fileName)
{
	if(parseFile(fileName) == 0)
		initialized = true;
	else
		initialized = false;
	return 0;
}

bool Config::isInitialized()
{
	return initialized;
}

int Config::parseFile(const string &fileName)
{
	ifstream ifs(fileName.c_str());
	//ifs.open();
	if(!ifs.is_open())
	{
		return 1;
	}
	string line;
	while(getline(ifs, line))
	{
		if(line.size() == 0)
			continue;
		if(line[0] == '#')
			continue;
		if(line[0] == '/' && line[1] == '/')
			continue;
		if(line.find("=") == string::npos)
			continue;
		string key, value;
		if(separateString(line, key, value) == 0)
		{
			if(key[0] == '$')
				varTable[key] = value;
			else
				confTable[key] = value;
		}
	}
	ifs.close();
	return 0;
}

void Config::replaceVariable(string &value)
{
	if(varTable.size() == 0)
		return;
	map<string, string>::iterator it;
	for(it = varTable.begin(); it != varTable.end(); it++)
	{
		size_t pos = value.find(it->first);
		if(pos == string::npos)
			continue;
		value.replace(pos, it->first.size(), it->second);
	}
	return;
}

int Config::separateString(const string &src, string &key, string &value)
{
	size_t pos = src.find("=");
	if(pos == string::npos)
		return 1;
	string keyTmp = src.substr(0, pos);
	string valueTmp = src.substr(pos+1);
	size_t p1 = keyTmp.find_first_not_of(Config::DELIMITERS);
	size_t p2 = keyTmp.find_last_not_of(Config::DELIMITERS);
	if(p1 == string::npos || p2 == string::npos)
		return 2;
	key = keyTmp.substr(p1, p2-p1+1);
	size_t p3 = valueTmp.find_first_not_of(Config::DELIMITERS);
	size_t p4 = valueTmp.find_last_not_of(Config::DELIMITERS);
	if(p3 == string::npos || p4 == string::npos)
		value = "";
	else
		value = valueTmp.substr(p3, p4-p3+1);
	return 0;
}

void Config::getAllKeys(vector<string> &keys)
{
	map<string, string>::iterator it;
	for(it = confTable.begin(); it != confTable.end(); it++)
	{
		keys.push_back(it->first);
	}
}
void Config::addConfigItem(const string &key, const string &value)
{
	confTable[key] = value;
}
